package jnpf.database.datatype.sync.enums;

import jnpf.constant.MsgCode;
import jnpf.database.datatype.db.*;
import jnpf.database.datatype.db.interfaces.DtInterface;
import jnpf.database.datatype.sync.model.DtConvertModel;
import jnpf.database.source.DbBase;
import jnpf.exception.DataException;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 数据库相对应一些方法
 *
 * @author JNPF开发平台组 YanYu
 * @version V3.3
 * @copyright 引迈信息技术有限公司
 * @date 2022-05-31
 */
@Getter
@AllArgsConstructor
public enum DtConvertEnum {

    /**
     * 根据数据选择的方法枚举
     */
    MYSQL(DbBase.MYSQL){
        /**
         * DtMutualConvertEnum(默认原始搭配)，在这个地方重复的枚举会重写覆盖原来的搭配
         */
        @Override
        public <T extends DtInterface> DtConvertModel<T> getModel(T dtEnum){
            DtConvertModel<T> model = new DtConvertModel<>();
            switch ((DtMySQLEnum)dtEnum){
                /* =============================== 整数类型 =============================== */
                case TINY_INT: convert(model,   DtConvertFixEnum.TINY_INT    ); break;
                case SMALL_INT: convert(model,  DtConvertFixEnum.SMALL_INT   ); break;
                case MEDIUM_INT:
                case INT: convert(model,        DtConvertFixEnum.INT         ); break;
                case BIGINT: convert(model,     DtConvertFixEnum.BIGINT      ); break;
                // ====================== 文本字符串类型:  ==========================;==== */
                // mysql(varchar) 转成 oracle(NVARCHAR2)、sqlserver(nvarchar;
                case CHAR: convert(model,       DtConvertFixEnum.CHAR        ); break;
                // mysql(test) 转成 oracle(NCLOB)、sqlserver(nvarchar(max); break;
                case BIT:
                case BLOB:
                case VARCHAR: convert(model,    DtConvertFixEnum.VARCHAR     ); break;
                case TINY_TEXT:
                case TEXT:
                case MEDIUM_TEXT:
                case LONG_TEXT: convert(model,  DtConvertFixEnum.TEXT        ); break;
                // ====================== 浮点类型:  =============================;= */
                case FLOAT: convert(model,      DtConvertFixEnum.FLOAT       ); break;
                case DOUBLE: convert(model,     DtConvertFixEnum.DOUBLE      ); break;
                // ====================== 定点数类型:  ============================;== */
                case DECIMAL: convert(model,    DtConvertFixEnum.DECIMAL     ); break;
                // ====================== 日期时间类型:  ===========================;=== */
                case YEAR:
                case DATE: convert(model,       DtConvertFixEnum.DATE        ); break;
                case TIME: convert(model,       DtConvertFixEnum.TIME        ); break;
                case DATE_TIME: convert(model,  DtConvertFixEnum.DATE_TIME   ); break;
                case TIMESTAMP: convert(model,  DtConvertFixEnum.TIMESTAMP   ); break;
                default:
            }
            model.setDtMySQLEnum((DtMySQLEnum) dtEnum);
            return model;
        }
    },
    ORACLE(DbBase.ORACLE){
        @Override
        public <T extends DtInterface> DtConvertModel<T> getModel(T dtEnum){
            DtConvertModel<T> model = new DtConvertModel<>();
            switch ((DtOracleEnum)dtEnum){
                /* =============================== 整数类型 =============================== */
                case NUMBER: convert(model,    DtConvertFixEnum.BIGINT      ); break;
                // ===================== 文本字符串类型 ===========================;= */
                case CHAR:
                case NCHAR: convert(model,     DtConvertFixEnum.CHAR        ); break;
                case VARCHAR2: convert(model,  DtConvertFixEnum.VARCHAR     ); break;
                case NVARCHAR2: convert(model, DtConvertFixEnum.NVARCHAR2   ); break;
                case CLOB: convert(model,      DtConvertFixEnum.TEXT        ); break;
                case NCLOB: convert(model,     DtConvertFixEnum.NCLOB       ); break;
                // ===================== 浮点类型  =============================;*/
                case FLOAT: convert(model,     DtConvertFixEnum.FLOAT       ); break;
                // ===================== 日期时间类型:  ==========================;== */
                case DATE: convert(model,      DtConvertFixEnum.DATE        ); break;
                case TIMESTAMP: convert(model, DtConvertFixEnum.TIMESTAMP   ); break;
                default:
            }
            model.setDtOracleEnum((DtOracleEnum) dtEnum);
            return model;
        }
    },
    SQL_SERVER(DbBase.SQL_SERVER){
        @Override
        public <T extends DtInterface> DtConvertModel<T> getModel(T dtEnum){
            DtConvertModel<T> model = new DtConvertModel<>();
            switch ((DtSQLServerEnum)dtEnum){
                /* =============================== 整数类型 =============================== */
                case TINY_INT: convert(model,       DtConvertFixEnum.TINY_INT    ); break;
                case SMALL_INT: convert(model,      DtConvertFixEnum.SMALL_INT   ); break;
                case INT: convert(model,            DtConvertFixEnum.INT         ); break;
                case BIGINT: convert(model,         DtConvertFixEnum.BIGINT      ); break;
                // ================== 文本字符串类型 =============================== *;
                case CHAR: convert(model,           DtConvertFixEnum.CHAR        ); break;
                case NVARCHAR: convert(model,       DtConvertFixEnum.NVARCHAR2   ); break;
                case VARCHAR: convert(model,        DtConvertFixEnum.VARCHAR     ); break;
                case VARCHAR_MAX:
                case IMAGE:
                case TEXT: convert(model,           DtConvertFixEnum.TEXT        ); break;
                case N_TEXT: convert(model,         DtConvertFixEnum.NCLOB       ); break;
                // ================== 日期时间类型:  =============================== *;
                case DATE: convert(model,           DtConvertFixEnum.DATE        ); break;
                case TIME: convert(model,           DtConvertFixEnum.TIME        ); break;
                case DATE_TIME: convert(model,      DtConvertFixEnum.DATE_TIME   ); break;
                case TIMESTAMP: convert(model,      DtConvertFixEnum.TIMESTAMP   ); break;
                // ===================== 浮点类型  =============================;*/
                case FLOAT: convert(model,          DtConvertFixEnum.FLOAT       ); break;
                // ================== 定点数类型:  =============================== *;
                case DECIMAL: convert(model,        DtConvertFixEnum.DECIMAL     ); break;
                default:
            }
            model.setDtSQLServerEnum((DtSQLServerEnum) dtEnum);
            return model;
        }
    },
    DM(DbBase.DM){
        @Override
        public <T extends DtInterface> DtConvertModel<T> getModel(T dtEnum){
            DtConvertModel<T> model = new DtConvertModel<>();
            switch ((DtDMEnum)dtEnum){
                /* =============================== 整数类型 =============================== */
                case TINY_INT: convert(model,      DtConvertFixEnum.TINY_INT    ); break;
                case SMALL_INT: convert(model,     DtConvertFixEnum.SMALL_INT   ); break;
                case INT: convert(model,           DtConvertFixEnum.INT         ); break;
                case BIGINT: convert(model,        DtConvertFixEnum.BIGINT      ); break;
                // ===================== 文本字符串类型 ===========================*;
                case CHAR: convert(model,          DtConvertFixEnum.CHAR        ); break;
                case VARCHAR: convert(model,       DtConvertFixEnum.VARCHAR     ); break;
                case VARCHAR2: convert(model,      DtConvertFixEnum.NVARCHAR2   ); break;
                case TEXT:
                case CLOB: convert(model,          DtConvertFixEnum.TEXT        ); break;
                // ===================== 时间格式 ===========================*;
                case DATE: convert(model,          DtConvertFixEnum.DATE        ); break;
                case DATE_TIME: convert(model,     DtConvertFixEnum.DATE_TIME   ); break;
                case TIME_STAMP: convert(model,    DtConvertFixEnum.TIMESTAMP   ); break;
                case TIME: convert(model,          DtConvertFixEnum.TIME        ); break;
                // =====================浮点===========================*;
                case FLOAT: convert(model,         DtConvertFixEnum.FLOAT       ); break;
                case DOUBLE: convert(model,        DtConvertFixEnum.DOUBLE      ); break;
                // ===================== 定点型 ===========================*
                case DECIMAL:
                case DEC: convert(model,           DtConvertFixEnum.DECIMAL     ); break;
                default:
            }
            model.setDtDMEnum((DtDMEnum) dtEnum);
            return model;
        }
    },
    KINGBASE(DbBase.KINGBASE_ES){
        @Override
        public <T extends DtInterface> DtConvertModel<T> getModel(T dtEnum){
            DtConvertModel<T> model = new DtConvertModel<>();
            switch ((DtKingbaseESEnum)dtEnum){
                /* =============================== 整数类型 =============================== */
                case TINY_INT: convert(model,      DtConvertFixEnum.TINY_INT    ); break;
                case SMALL_INT: convert(model,     DtConvertFixEnum.SMALL_INT   ); break;
                case INTEGER: convert(model,       DtConvertFixEnum.INT         ); break;
                case BIGINT: convert(model,        DtConvertFixEnum.BIGINT      ); break;
                // ============= 文本字符串类型 ===========================*;
                case CHAR: convert(model,          DtConvertFixEnum.CHAR        ); break;
                case VARCHAR: convert(model,       DtConvertFixEnum.VARCHAR     ); break;
                case TEXT: convert(model,          DtConvertFixEnum.TEXT        ); break;
                // =============浮点===========================*;
                case FLOAT4: convert(model,        DtConvertFixEnum.FLOAT       ); break;
                case FLOAT8: convert(model,        DtConvertFixEnum.DOUBLE      ); break;
                // =================== 定点型 ========================================*;
                case NUMERIC: convert(model,       DtConvertFixEnum.DECIMAL     ); break;
                // ============= 时间格式 ===========================*;
                case TIME: convert(model,          DtConvertFixEnum.TIME        ); break;
                case DATE: convert(model,          DtConvertFixEnum.DATE        ); break;
                case DATE_TIME: convert(model,     DtConvertFixEnum.DATE_TIME   ); break;
                case TIMESTAMP: convert(model,     DtConvertFixEnum.TIMESTAMP   ); break;
                default:
            }
            model.setDtKingbaseESEnum((DtKingbaseESEnum) dtEnum);
            return model;
        }
    },
    POSTGRE(DbBase.POSTGRE_SQL){
        @Override
        public <T extends DtInterface> DtConvertModel<T> getModel(T dtEnum){
            DtConvertModel<T> model = new DtConvertModel<>();
            switch ((DtPostgreSQLEnum)dtEnum){
                /* =============================== 整数类型 =============================== */
                case INT2: convert(model,      DtConvertFixEnum.SMALL_INT   ); break;
                case INT4: convert(model,      DtConvertFixEnum.INT         ); break;
                case INT8: convert(model,      DtConvertFixEnum.BIGINT      ); break;
                // =================== 文本字符串类型 =======================================*/
                case CHAR: convert(model,      DtConvertFixEnum.CHAR        ); break;
                case VARCHAR: convert(model,   DtConvertFixEnum.VARCHAR     ); break;
                case TEXT: convert(model,      DtConvertFixEnum.TEXT        ); break;
                // ====================浮点===========================*/
                case FLOAT4: convert(model,    DtConvertFixEnum.FLOAT       ); break;
                case FLOAT8: convert(model,    DtConvertFixEnum.DOUBLE      ); break;
                // =================== 定点型 =======================================*/
                case NUMERIC: convert(model,   DtConvertFixEnum.DECIMAL     ); break;
                // =================== 时间日期 =======================================*/
                case TIME: convert(model,      DtConvertFixEnum.TIME        ); break;
                case DATE: convert(model,      DtConvertFixEnum.DATE        ); break;
                case TIMESTAMP: convert(model, DtConvertFixEnum.TIMESTAMP   ); break;
                default:
            }
            model.setDtPostgreSQLEnum((DtPostgreSQLEnum)dtEnum);
            return model;
        }
    };

    private final String dbType;

    /**
     * 获取转换模型
     * @param fromDtEnum 数据类型枚举
     * @return 转换模型
     * @throws DataException ignore
     */
    public static <T extends DtInterface> DtConvertModel<T> getConvertModel(T fromDtEnum) throws DataException {
        return choose(fromDtEnum.getDbType()).getModel(fromDtEnum);
    }

    /* ================================ 内部方法 ================================== */

    /**
     * 根据数据类型枚举获取对应转换模型
     * @param dtEnum 数据类型枚举
     * @return ignore
     */
    protected  <T extends DtInterface> DtConvertModel<T> getModel(T dtEnum){return null;}

    /**
     * 根据数据获取其转换规则枚举
     * @param dbType 数据库类型
     * @return 转换规则枚举
     * @throws DataException ignore
     */
    private static DtConvertEnum choose(String dbType) throws DataException {
        for (DtConvertEnum dbMethod : DtConvertEnum.values()) {
            if(dbMethod.getDbType().equals(dbType)){
                return dbMethod;
            }
        }
        throw  new DataException(MsgCode.DB005.get());
    }

    /**
     * 内部特殊转换
     * @param model 转换模型
     * @param fixEnums 固定转换硬编码
     * @param convertDtEnums 特殊转换集合
     */
    protected <T extends DtInterface> void convert(DtConvertModel<T> model, DtConvertFixEnum fixEnums, DtInterface... convertDtEnums){
        try{
            // 互转关系中的转换关系
            for (DtInterface dtMutualConvert : fixEnums.getDtMutualConverts()) {
                model.setDtEnum(dtMutualConvert);
            }
            // 特殊的转换关系
            for (DtInterface convertDtEnum : convertDtEnums) {
                model.setDtEnum(convertDtEnum);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}
